/*
 * Copyright (c) 2020, yifengling0. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. No personal names or organizations' names associated with the
 *    Atomthreads project may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */


#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include "numberical.h"
#include <ctype.h>
#include <math.h>
#include <errno.h>

int IsHex( char *strSrc )
{
    int ret = 0;
    
    while( '\0' != *strSrc ){
        if( ( *strSrc < '0' || *strSrc > '9' )
        &&  ( *strSrc < 'A' || *strSrc > 'F' )
        &&  ( *strSrc < 'a' || *strSrc > 'f' ) ){
            ret = 1;
            break;
        }else{
            strSrc++;
        }
    }
    return ret;
}

int is_int(char* str)
{
	int len = strlen(str), i = 0;

	for (i = 0; i < len; i++)
	{
		if (!(isdigit(str[i])))
			return 0;
	}
	return 1;
}

int is_float(char *str) {
    char *endptr;
    errno = 0; 
    strtod(str, &endptr);
 
    
    if((errno == 0) && (*endptr == '\0')){
        return 1;
    }else
    {
        return 0;
    }
    
}

int IsDec( char *strSrc )
{
	int ret = 0;

	while( '\0' != *strSrc ){
		if( *strSrc < '0' || *strSrc > '9' ){
			ret = 1;
			break;
		}else{
			strSrc++;
		}	
	} 
	return ret;
}

int IsLimit( uint32_t dwSrc, uint32_t dwMin, uint32_t dwMax )
{
    int ret = 0;

    if( dwSrc < dwMin || dwSrc > dwMax ){
        ret = 1;
    }

    return ret;
}

static void hex_prefix(char* src, int c)
{
	size_t len = strlen(src);
    size_t remind = c - len;
	char dest[9] = {0};

	while (remind > 0) {
		dest[remind - 1] = '0';
		remind--;
	}

	strcat(dest, src);
	strcpy(src, dest);
}

void IntToString(unsigned long val, char *buf, unsigned radix, int prefix)
{
	char *p;/*pointer to traverse string*/
	char *firstdig;/*pointer to first digit*/
	char temp;/*temp char*/
	unsigned digval;/*value of digit*/
 
	p = buf;
	firstdig = p;/*save pointer to first digit*/
 
	do{
		digval =(unsigned)(val % radix);
		val /= radix;/*get next digit*/
 
		/*convert to ascii and store */
		if(digval > 9)
			*p++ = (char)(digval - 10 + 'A');/*a letter*/
		else 
			*p++ = (char)(digval + '0');/*a digit*/
	}while(val > 0);
 
		/*We now have the digit of the number in the buffer,but in reverse
		order.Thus we reverse them now.*/
 
	*p-- = '\0';/*terminate string;p points to last digit*/
 
	do{
		temp = *p;
		*p = *firstdig;
		*firstdig = temp;/*swap *p and *firstdig*/
		--p;
		++firstdig;/*advance to next two digits*/
	}while(firstdig < p);/*repeat until halfway*/

    if(prefix > 0){
      hex_prefix(buf, prefix);
    }
}


unsigned long StringToInt( char *strSrc )
{
    unsigned long	dwRet = 0;
    unsigned long	dwNum = 0;
    size_t  j       = 0;
    unsigned long	dwTemp   = 0;
    size_t iLen = strlen( strSrc );

    for( size_t idx = 0; idx < iLen; idx++ ){

        if( strSrc[idx] >= '0' && strSrc[idx] <= '9' ){
            dwNum = strSrc[idx] - '0';
        }

        dwTemp = dwNum;
        for( j = iLen - idx - 1; j > 0; j-- ){
            dwTemp *= 10;
        }
        dwRet += dwTemp;
    }
    return dwRet;
}


unsigned long  HexStringToInt( char *strSrc )
{
    unsigned long	dwRet = 0;
    unsigned long	dwNum = 0;
    size_t  j       = 0;
    unsigned long	dwTemp   = 0;
    size_t iLen = strlen( strSrc );

    for( size_t idx = 0; idx < iLen; idx++ ){

        if( strSrc[idx] >= '0' && strSrc[idx] <= '9' ){
            dwNum = strSrc[idx] - '0';

        }else if( strSrc[idx] >= 'A' && strSrc[idx] <= 'F' ){
            dwNum = strSrc[idx] - 'A' + 10;

        }else if( strSrc[idx] >= 'a' && strSrc[idx] <= 'f' ){
            dwNum = strSrc[idx] - 'a' + 10;

        }else{
            
        }
        dwTemp = dwNum;
        for( j = iLen - idx - 1; j > 0; j-- ){
            dwTemp *= 16;
        }
        dwRet += dwTemp;
    }
    return dwRet;
}

#define ZEROPAD     (1 << 0)    /* pad with zero */
#define SIGN        (1 << 1)    /* unsigned/signed long */
#define PLUS        (1 << 2)    /* show plus */
#define SPACE       (1 << 3)    /* space if plus */
#define LEFT        (1 << 4)    /* left justified */
#define SPECIAL     (1 << 5)    /* 0x */
#define LARGE       (1 << 6)    /* use 'ABCDEF' instead of 'abcdef' */

static int skip_atoi(const char **s)
{
    register int i = 0;
    while (isdigit(**s))
        i = i * 10 + *((*s)++) - '0';

    return i;
}

static int divide(long *n, int base)
{
    int res;

    /* optimized for processor which does not support divide instructions. */
    if (base == 10)
    {
        res = (int)(((unsigned long)*n) % 10U);
        *n = (long)(((unsigned long)*n) / 10U);
    }
    else
    {
        res = (int)(((unsigned long)*n) % 16U);
        *n = (long)(((unsigned long)*n) / 16U);
    }

    return res;
}

static char *_print_number(char *buf,
                          char *end,
                          long  num,
                          int   base,
                          int   s,
                          int   type)
{
    char c, sign;
    char tmp[16];

    const char *digits;
    static const char small_digits[] = "0123456789abcdef";
    static const char large_digits[] = "0123456789ABCDEF";
    register int i;
    register int size;

    size = s;

    digits = (type & LARGE) ? large_digits : small_digits;
    if (type & LEFT)
        type &= ~ZEROPAD;

    c = (type & ZEROPAD) ? '0' : ' ';

    /* get sign */
    sign = 0;
    if (type & SIGN)
    {
        if (num < 0)
        {
            sign = '-';
            num = -num;
        }
        else if (type & PLUS)
            sign = '+';
        else if (type & SPACE)
            sign = ' ';
    }
    
    if (type & SPECIAL)
    {
        if (base == 16)
            size -= 2;
        else if (base == 8)
            size--;
    }
    
    i = 0;
    if (num == 0)
        tmp[i++] = '0';
    else
    {
        while (num != 0)
            tmp[i++] = digits[divide(&num, base)];
    }

    size -= i;

    if (!(type & (ZEROPAD | LEFT)))
    {
        if ((sign) && (size > 0))
            size--;

        while (size-- > 0)
        {
            if (buf < end)
                *buf = ' ';
            ++ buf;
        }
    }

    if (sign)
    {
        if (buf < end)
        {
            *buf = sign;
        }
        -- size;
        ++ buf;
    }
    
    if (type & SPECIAL)
    {
        if (base == 8)
        {
            if (buf < end)
                *buf = '0';
            ++ buf;
        }
        else if (base == 16)
        {
            if (buf < end)
                *buf = '0';
            ++ buf;
            if (buf < end)
            {
                *buf = type & LARGE ? 'X' : 'x';
            }
            ++ buf;
        }
    }

    /* no align to the left */
    if (!(type & LEFT))
    {
        while (size-- > 0)
        {
            if (buf < end)
                *buf = c;
            ++ buf;
        }
    }

    /* put number in the temporary buffer */
    while (i-- > 0)
    {
        if (buf < end)
            *buf = tmp[i];
        ++ buf;
    }

    while (size-- > 0)
    {
        if (buf < end)
            *buf = ' ';
        ++ buf;
    }

    return buf;
}


uint32_t _vsnprintf(char *buf, uint32_t size, const char *fmt, va_list args)
{
    uint32_t num;
    int i, len;
    char *str, *end, c;
    const char *s;

    uint32_t base;            /* the base of number */
    uint32_t flags;           /* flags to print number */
    uint32_t qualifier;       /* 'h', 'l', or 'L' for integer fields */
    int32_t field_width;     /* width of output field */

    str = buf;
    end = buf + size;

    /* Make sure end is always >= buf */
    if (end < buf)
    {
        end  = ((char *) - 1);
        size = end - buf;
    }

    for (; *fmt ; ++fmt)
    {
        if (*fmt != '%')
        {
            if (str < end)
                *str = *fmt;
            ++ str;
            continue;
        }

        /* process flags */
        flags = 0;

        while (1)
        {
            /* skips the first '%' also */
            ++ fmt;
            if (*fmt == '-') flags |= LEFT;
            else if (*fmt == '+') flags |= PLUS;
            else if (*fmt == ' ') flags |= SPACE;
            else if (*fmt == '#') flags |= SPECIAL;
            else if (*fmt == '0') flags |= ZEROPAD;
            else break;
        }

        /* get field width */
        field_width = -1;
        if (isdigit(*fmt)) field_width = skip_atoi(&fmt);
        else if (*fmt == '*')
        {
            ++ fmt;
            /* it's the next argument */
            field_width = va_arg(args, int);
            if (field_width < 0)
            {
                field_width = -field_width;
                flags |= LEFT;
            }
        }

        /* get the conversion qualifier */
        qualifier = 0;

        if (*fmt == 'h' || *fmt == 'l')
        {
            qualifier = *fmt;
            ++ fmt;

        }

        /* the default base */
        base = 10;

        switch (*fmt)
        {
        case 'c':
            if (!(flags & LEFT))
            {
                while (--field_width > 0)
                {
                    if (str < end) *str = ' ';
                    ++ str;
                }
            }

            /* get character */
            c = (uint8_t)va_arg(args, int);
            if (str < end) *str = c;
            ++ str;

            /* put width */
            while (--field_width > 0)
            {
                if (str < end) *str = ' ';
                ++ str;
            }
            continue;

        case 's':
            s = va_arg(args, char *);
            if (!s) s = "(NULL)";

            len = strlen(s);

            if (!(flags & LEFT))
            {
                while (len < field_width--)
                {
                    if (str < end) *str = ' ';
                    ++ str;
                }
            }

            for (i = 0; i < len; ++i)
            {
                if (str < end) *str = *s;
                ++ str;
                ++ s;
            }

            while (len < field_width--)
            {
                if (str < end) *str = ' ';
                ++ str;
            }
            continue;

        case 'p':
            if (field_width == -1)
            {
                field_width = sizeof(void *) << 1;
                flags |= ZEROPAD;
            }

            str = _print_number(str, end,
                               (long)va_arg(args, void *),
                               16, field_width, flags);
            continue;

        case '%':
            if (str < end) *str = '%';
            ++ str;
            continue;

        /* integer number formats - set up the flags and "break" */
        case 'o':
            base = 8;
            break;

        case 'X':
            flags |= LARGE;
        case 'x':
            base = 16;
            break;

        case 'd':
        case 'i':
            flags |= SIGN;
        case 'u':
            break;

        default:
            if (str < end) *str = '%';
            ++ str;

            if (*fmt)
            {
                if (str < end) *str = *fmt;
                ++ str;
            }
            else
            {
                -- fmt;
            }
            continue;
        }

        if (qualifier == 'l')
        {
            num = va_arg(args, uint32_t);
            if (flags & SIGN) num = (int32_t)num;
        }
        else if (qualifier == 'h')
        {
            num = (uint16_t)va_arg(args, uint32_t);
            if (flags & SIGN) num = (int16_t)num;
        }
        else
        {
            num = va_arg(args, uint32_t);
            if (flags & SIGN) num = (int32_t)num;
        }

        str = _print_number(str, end, num, base, field_width, flags);
    }

    if (size > 0)
    {
        if (str < end) *str = '\0';
        else
        {
            end[-1] = '\0';
        }
    }

    /* the trailing null byte doesn't count towards the total
    * ++str;
    */
    return str - buf;
}

uint32_t l_vsnprintf(char *buf, uint32_t size, const char *format, ... )
{
    uint32_t ret = 0;
    va_list ap;
    va_start(ap, format);
    ret = _vsnprintf(buf, size, format, ap);
    va_end(ap);

    return ret;
}

void    ToFloatNumber(FloatNumber *number, float input, int factor)
{
    int calcFactor = (int)pow(10, factor);

    int result = (int)(input * calcFactor + (input < 0 ? -0.5 : 0.5));

    number->d = (abs)(result) / calcFactor;
    number->i = abs(result) % calcFactor;

    number->f = input < 0 ? '-' : ' ';

    number->ic = factor;
}